package cn.com.myspring.framework.context;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import cn.com.myspring.framework.annotation.MYAutowired;
import cn.com.myspring.framework.annotation.MYController;
import cn.com.myspring.framework.annotation.MYService;
import cn.com.myspring.framework.aop.MYAopProxy;
import cn.com.myspring.framework.aop.MYCglibAopProxy;
import cn.com.myspring.framework.aop.MYJdkDynamicAopProxy;
import cn.com.myspring.framework.aop.config.MYAopConfig;
import cn.com.myspring.framework.aop.support.MYAdvisedSupport;
import cn.com.myspring.framework.beans.MYBeanWrapper;
import cn.com.myspring.framework.beans.config.MYBeanDefinition;
import cn.com.myspring.framework.beans.config.MYBeanPostProcessor;
import cn.com.myspring.framework.beans.support.MYBeanDefinitionReader;
import cn.com.myspring.framework.context.support.MYAbstractApplicationContext;
import cn.com.myspring.framework.core.MYBeanFactory;

public class MYApplicationContext extends MYAbstractApplicationContext implements MYBeanFactory {

    private String[] configLoactions;

    private MYBeanDefinitionReader reader;

    // 单例的IOC容器缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();

    // 通用的IOC容器
    private Map<String, MYBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, MYBeanWrapper>();

    public MYApplicationContext(String...configLoactions) {
        this.configLoactions = configLoactions;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void refresh() throws Exception {

        // 1.初始化Bean工厂
        obtainFreshBeanFactory();

        // 2、定位，定位配置文件
        reader = new MYBeanDefinitionReader(this.configLoactions);

        // 3、加载配置文件，扫描相关的类，把它们封装成BeanDefinition
        List<MYBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        // 4、注册，把配置信息放到容器里面(伪IOC容器)
        doRegisterBeanDefinition(beanDefinitions);

        // 5、把不是延时加载的类，有提前初始化
        doAutowrited();
    }

    private void obtainFreshBeanFactory() {
        super.createBeanFactory();
    }

    private void doRegisterBeanDefinition(List<MYBeanDefinition> beanDefinitions) throws Exception {
        for (MYBeanDefinition beanDefinition : beanDefinitions) {
            if (super.getBeanFactory().getBeanDefinitionMap().containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The[" + beanDefinition.getFactoryBeanName() + "] is exists!!");
            }
            // 将bean定义注册到bean工厂中
            super.getBeanFactory().getBeanDefinitionMap().put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    private void doAutowrited() {
        for (Map.Entry<String, MYBeanDefinition> beanDefinitionEntry : super.getBeanFactory().getBeanDefinitionMap()
                .entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 依赖注入，从这里开始，通过读取BeanDefinition中的信息
    // 然后，通过反射机制创建一个实例并返回
    // Spring做法是，不会把最原始的对象放出去，会用一个BeanWrapper来进行一次包装
    // 装饰器模式：
    // 1、保留原来的OOP关系
    // 2、我需要对它进行扩展，增强（为了以后AOP打基础）
    @Override
    public Object getBean(String beanName) throws Exception {

        // 获取bean定义
        MYBeanDefinition myBeanDefinition = super.getBeanFactory().getBeanDefinitionMap().get(beanName);
        Object instance = null;

        // 初始化bean前置操作
        MYBeanPostProcessor postProcessor = new MYBeanPostProcessor();
        postProcessor.postProcessBeforeInitialization(instance, beanName);

        // 初始化bean
        instance = instantiateBean(beanName, myBeanDefinition);

        // 3、把这个对象封装到BeanWrapper中
        MYBeanWrapper beanWrapper = new MYBeanWrapper(instance);

        // 4、把BeanWrapper存到IOC容器里面
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);

        postProcessor.postProcessAfterInitialization(instance, beanName);

        // //3、注入
        populateBean(beanName, new MYBeanDefinition(), beanWrapper);

        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }

    private Object instantiateBean(String beanName, MYBeanDefinition myBeanDefinition) {
        // 1、拿到要实例化的对象的类名
        String className = myBeanDefinition.getBeanClassName();

        // 2、反射实例化，得到一个对象
        Object instance = null;
        try {
            // 是否为单例
            if (this.singletonObjects.containsKey(className)) {
                instance = this.singletonObjects.get(className);
            } else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();

                // 判断该bean是否被代理
                MYAdvisedSupport ms = new MYAdvisedSupport(getAopConfig());
                ms.setTarget(instance);
                ms.setTargetClass(clazz);
                if(ms.pointCutMatch()) {
                    instance = createProxy(ms).getProxy();
                }
                this.singletonObjects.put(className, instance);
                this.singletonObjects.put(myBeanDefinition.getFactoryBeanName(), instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return instance;
    }

    
    /**
     * @Description 获取AOP配置
     * @return
     */
    private MYAopConfig getAopConfig() {
        MYAopConfig config = new MYAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return config;
    }
    
    /**
     * @Description 创建aop代理
     * @param config
     * @return
     */
    private MYAopProxy createProxy(MYAdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        // 判断类是否有实现接口
        if(targetClass.getInterfaces().length > 0){
            return new MYJdkDynamicAopProxy(config);
        }
        return new MYCglibAopProxy(config);
    }

    private void populateBean(String beanName, MYBeanDefinition myBeanDefinition, MYBeanWrapper myBeanWrapper) {
        Object instance = myBeanWrapper.getWrappedInstance();

        Class<?> clazz = myBeanWrapper.getWrappedClass();
        // 判断只有加了注解的类，才执行依赖注入
        if (!(clazz.isAnnotationPresent(MYController.class) || clazz.isAnnotationPresent(MYService.class))) {
            return;
        }

        // 获得所有的fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 字段是否增加MYAutowired注解
            if (!field.isAnnotationPresent(MYAutowired.class)) {
                continue;
            }

            MYAutowired autowired = field.getAnnotation(MYAutowired.class);

            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }

            // 强制访问
            field.setAccessible(true);

            try {
                if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                    continue;
                }
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }

    }

    @Override
    public Object getBean(Class<?> beanClass) throws Exception {
        return getBean(toLowerFirstCase(beanClass.getSimpleName()));
    }

    /**
     * @Description 将简单类名首字母转换为小写
     * @param simpleName
     * @return
     */
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    public String[] getBeanDefinitionNames() {
        int len = super.getBeanFactory().getBeanDefinitionMap().size();
        String[] beanNames = super.getBeanFactory().getBeanDefinitionMap().keySet().toArray(new String[len]);
        return beanNames;
    }

    public Properties getConfig() {
        return reader.getConfig();
    }
}
